import { RetResult } from "@/ecjrts/tools/modHelper";
import { BaseIO } from "@/ecjrts/mode/baseIO";
import { cloneDeep, merge } from "lodash";
export interface PinState {}
export interface PinCapability {}
export interface PinSetting {
  minPinLen: number;
  maxPinLen: number;
  autoEnd: boolean;
  pinPading: string;
  maxLenUserEntry: number;
  pinSupportKeys: PinSupportKeys[];
  backspaceToClear: boolean;
  useSM4: boolean;
}
export type UserEntryRet =
  | "EntryComplete"
  | "Timeout"
  | "FatalError"
  | "DeviceError";
export type BufferPINRet =
  | "PinBuffered"
  | "Timeout"
  | "FatalError"
  | "DeviceError";
export type FormatPinRet =
  | "PinFormatted"
  | "NoPin"
  | "FatalError"
  | "DeviceError";
export type UserEntryFnRet = "KeyPressed";
export type BufferPINFnRet = UserEntryFnRet;
export type UserEntryFn = (param: RetResult<UserEntryFnRet>) => void;
export type BufferPINFn = (param: RetResult<BufferPINFnRet>) => void;
export type PinMethod =
  | "UserEntry"
  | "CancelUserEntry"
  | "CancelPINEntry"
  | "FormatPin"
  | "BufferPIN"
  | "-";
export type PinSupportKeys =
  | "NUMBERS"
  | "ENTER"
  | "CANCEL"
  | "CLEAR"
  | "F1"
  | "F2"
  | "F3"
  | "F4"
  | "F5"
  | "F6"
  | "F7"
  | "F8";
export type PinAlgorithms = (
  | "ECB"
  | "CBC"
  | "CFB"
  | "RSA"
  | "ECMA"
  | "MAC"
  | "SMAC"
  | "TRIDESECB"
  | "TRIDESCBC"
  | "TRIDESCFB"
  | "MAAMAC"
  | "SM4"
  | "SM4MAC"
)[];

const defaultConfig: PinSetting = {
  minPinLen: 4,
  maxPinLen: 6,
  autoEnd: true,
  pinPading: "00",
  maxLenUserEntry: 0,
  pinSupportKeys: [
    "NUMBERS",
    "ENTER",
    "CANCEL",
    "CLEAR",
    "F1",
    "F2",
    "F3",
    "F4",
    "F5",
    "F6",
    "F7",
    "F8",
  ],
  backspaceToClear: false,
  useSM4: false,
};
// this.param = { 'c_minPinLen': '4', 'c_maxPinLen': '6', 'c_autoEnd': '1', 'c_pin_pading': '00', 'c_maxlen_userEntry': 0, 'c_pin_support_keys': 'NUMBERS,ENTER,CANCEL,CLEAR,F1,F2,F3,F4,F5,F6,F7,F8', 'c_backspace_to_clear': '0', 'c_use_SM4': '0' };
export class Pin extends BaseIO<PinState, PinCapability> {
  private currenMethod: PinMethod;
  private configs: PinSetting;
  private cpSM4: boolean;
  private cpSM4MAC: boolean;
  constructor(modName: string, mod: any) {
    super(modName, mod);
    this.currenMethod = "-";
    this.configs = cloneDeep(defaultConfig);
    this.cpSM4 = false;
    this.cpSM4MAC = false;
    const init = (...args: any[]) => {
      this.isOpen = true;
      this.remove("OpenCompleted", init);
      this.remove("ConnectionOpened", init);
      Trace(EC_INFO, `<--${this.modName}:init`);
      this.CancelUserEntry();
      this.CancelPINEntry();
    };
    this.add("OpenCompleted", init);
    this.add("ConnectionOpened", init);
  }

  async UserEntry(
    __call_fn: UserEntryFn | null = null
  ): Promise<void | RetResult<UserEntryRet>> {
    if (!this.isOpen) return;
    Trace(EC_INFO, `-->${this.modName}:UserEntry`);
    // let param = Object.assign({}, this.param);

    // this.opState = "UserEntry";
    let CpKeysSupported = "NUMBERS,ENTER,CANCEL,CLEAR";
    let max = this.configs.maxLenUserEntry;
    let bAutoEnd = max > 0;
    if (this.mod.strKeySupported == "UNKNOWN") {
      CpKeysSupported = this.configs.pinSupportKeys.toString();
      // CpKeysSupported = 'NUMBERS,ENTER,CANCEL,CLEAR';
    } else {
      CpKeysSupported = this.mod.strKeySupported;
    }
    this.setCurrentMethod("UserEntry");
    this.currenMethod = "UserEntry";
    this.removeMethodBindEvt("UserEntry");
    return new Promise<RetResult<UserEntryRet>>((resolve, reject) => {
      const KeyPressed = (...args: any[]) => {
        // Trace(EC_INFO, '<--${this.modName}:UserEntry FatalError');
        if (__call_fn)
          __call_fn({ mod: this.modName, ret: "KeyPressed", param: args });
      };
      const EntryComplete = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:UserEntry EntryComplete`);
        resolve({ mod: this.modName, ret: "EntryComplete", param: args });
      };
      const Cancelled = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:UserEntry Cancelled`);
        reject({ mod: this.modName, ret: "Cancelled", param: args });
      };
      const FatalError = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:UserEntry FatalError`);
        resolve({ mod: this.modName, ret: "FatalError", param: args });
      };
      const DeviceError = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:UserEntry DeviceError`);
        resolve({ mod: this.modName, ret: "DeviceError", param: args });
      };
      const Timeout = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:UserEntry Timeout`);
        resolve({ mod: this.modName, ret: "Timeout", param: args });
      };

      //   this.RemoveAll();
      this.add("KeyPressed", KeyPressed);
      this.add("EntryComplete", EntryComplete);
      this.add("Cancelled", Cancelled);
      this.add("Timeout", Timeout);
      this.add("FatalError", FatalError);
      this.add("DeviceError", DeviceError);

      this.mod.UserEntry(
        max,
        bAutoEnd,
        CpKeysSupported,
        "",
        0,
        (ret: string) => {
          if (parseInt(ret) != 0) {
            Trace(EC_INFO, `<--${this.modName}:UserEntry ret=` + ret);
            resolve({ mod: this.modName, ret: "FatalError", param: [] });
          }
        }
      );
    })
      .then((value) => {
        this.removeMethodBindEvt("UserEntry");
        __call_fn = null;
        return value;
      })
      .catch(() => {});
  }

  CancelUserEntry() {
    if (!this.isOpen) return Promise.resolve("Cancelled");
    return new Promise<RetResult<"Cancelled">>((resolve, reject) => {
      const Cancelled = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:Cancelled EntryComplete`);
        this.removeMethodBindEvt("CancelUserEntry");
        resolve({ mod: this.modName, ret: "Cancelled", param: args });
      };
      this.currenMethod = "CancelUserEntry";
      this.setCurrentMethod("CancelUserEntry");
      this.removeMethodBindEvt("CancelUserEntry");
      this.add("Cancelled", Cancelled);
      Trace(EC_INFO, `-->${this.modName}:CancelUserEntry`);
      this.mod.CancelUserEntry();
    });
  }

  /**
   *	获取按键
   * @param    键值
   * @return   0  成功
   * @return   -1  失败
   */
  async BufferPIN(
    actvekey: string,
    __call_fn: BufferPINFn | null
  ): Promise<void | RetResult<BufferPINRet>> {
    if (!this.isOpen) return;
    Trace(EC_INFO, `-->${this.modName}:BufferPIN`);
    // let param = Object.assign({}, this.param);
    let funcKey = "";
    // 去除不支持的左右功能键
    if (actvekey) {
      let CpKeysSupported =
        "NUMBERS,ENTER,CANCEL,CLEAR,F1,F2,F3,F4,F5,F6,F7,F8";

      if (this.mod.strKeySupported === "UNKNOWN") {
        CpKeysSupported = this.configs.pinSupportKeys.toString();
      } else {
        CpKeysSupported = this.mod.strKeySupported;
      }

      let ary = actvekey.split(",");
      let fun = [];
      CpKeysSupported += ",";
      for (let i = 0; i < ary.length; i++) {
        if (CpKeysSupported.indexOf(ary[i] + ",") >= 0) fun.push(ary[i]);
      }
      if (fun.length > 0) {
        funcKey = "," + fun.join(",");
      }
    }

    var atuoend = this.configs.autoEnd ? 1 : 0;
    var activeKey = "NUMBERS,ENTER,CANCEL,CLEAR" + funcKey;
    if (this.configs.backspaceToClear) {
      activeKey = activeKey.replace("CLEAR", "BACKSPACE");
    }
    this.setCurrentMethod("UserEntry");
    this.currenMethod = "BufferPIN";
    this.removeMethodBindEvt("UserEntry");
    return new Promise<RetResult<BufferPINRet>>((resolve, reject) => {
      const KeyPressed = (...args: any[]) => {
        if (__call_fn)
          __call_fn({ mod: this.modName, ret: "KeyPressed", param: args });
      };
      const PinBuffered = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:BufferPIN PinBuffered`);
        resolve({ mod: this.modName, ret: "PinBuffered", param: args });
      };
      const PinCancelled = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:BufferPIN PinCancelled`);
        reject({ mod: this.modName, ret: "PinCancelled", param: args });
      };
      const Timeout = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:BufferPIN Timeout`);
        resolve({ mod: this.modName, ret: "Timeout", param: args });
      };
      const FatalError = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:BufferPIN FatalError`);
        resolve({ mod: this.modName, ret: "FatalError", param: args });
      };
      const DeviceError = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:BufferPIN DeviceError`);
        resolve({ mod: this.modName, ret: "DeviceError", param: args });
      };

      // this.RemoveAll();
      this.add("KeyPressed", KeyPressed);
      this.add("PinBuffered", PinBuffered);
      this.add("PinCancelled", PinCancelled);
      this.add("Timeout", Timeout);
      this.add("FatalError", FatalError);
      this.add("DeviceError", DeviceError);
      this.mod.BufferPIN(
        this.configs.minPinLen,
        this.configs.maxPinLen,
        atuoend,
        activeKey,
        "ENTER,CANCEL",
        0,
        (ret: string) => {
          if (parseInt(ret) != 0) {
            Trace(EC_INFO, `<--${this.modName}:BufferPIN ret=` + ret);
            resolve({ mod: this.modName, ret: "FatalError", param: [] });
          }
        }
      );
    })
      .then((value) => {
        __call_fn = null;
        this.removeMethodBindEvt("UserEntry");
        return value;
      })
      .catch(() => {});
  }
  /**
   *	撤销输入
   * @param    无参
   * @return   无
   */
  CancelPINEntry() {
    if (!this.isOpen) return;
    Trace(EC_INFO, `-->${this.modName}:CancelPINEntry`);
    this.currenMethod = "CancelPINEntry";
    this.mod.CancelPINEntry();
  }

  GetCurrentMethodName() {
    return this.currenMethod;
  }
  SetCurrentMethodName(s: PinMethod) {
    this.currenMethod = s;
  }

  async FormatPin(pan: string, needPad: boolean) {
    if (!this.isOpen) return;
    Trace(EC_INFO, `-->${this.modName}:FormatPin`);
    let strPanData = pan; // 采用自定义的PanData获取pinblock PanData根据配置决定是否截取为12位
    if (strPanData) {
      let len = strPanData.length;
      if (len > 12) {
        strPanData = strPanData.substring(len - 13, len - 1);
      } else {
        if (needPad) {
          strPanData = "000000000000" + strPanData + "0";
          len = strPanData.length;
          strPanData = strPanData.substring(len - 13, len - 1);
        }
      }
    } else {
      strPanData = "";
    }

    this.currenMethod = "FormatPin";

    // let pading = __strToHex(defaultConfig.pinPading);
    let keyName = "PINKey"; // 国密默认密钥名
    let Format = "ANSI";
    let pad = 0x0f;

    return new Promise<RetResult<FormatPinRet>>((resolve, reject) => {
      const PinFormatted = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:FormatPin PinFormatted`);
        resolve({ mod: this.modName, ret: "PinFormatted", param: args });
      };
      const NoPin = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:FormatPin NoPin`);
        resolve({ mod: this.modName, ret: "NoPin", param: args });
      };
      const FatalError = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:FormatPin FatalError`);
        resolve({ mod: this.modName, ret: "FatalError", param: args });
      };
      const DeviceError = (...args: any[]) => {
        Trace(EC_INFO, `<--${this.modName}:FormatPin DeviceError`);
        resolve({ mod: this.modName, ret: "DeviceError", param: args });
      };
      this.add("PinFormatted", PinFormatted);
      this.add("NoPin", NoPin);
      this.add("FatalError", FatalError);
      this.add("DeviceError", DeviceError);
      if (this.cpSM4 && this.configs.useSM4) {
        this.mod.FormatPinEx(
          Format,
          strPanData,
          pad,
          keyName,
          "",
          "",
          (ret: string) => {
            if (parseInt(ret) !== 0) {
              Trace(EC_INFO, "<--FormatPin ret=" + ret);
              resolve({ mod: this.modName, ret: "FatalError", param: [] });
            }
          }
        );
      } else {
        this.mod.FormatPIN(
          Format,
          strPanData,
          pad,
          keyName,
          "",
          "",
          (ret: string) => {
            if (parseInt(ret) !== 0) {
              Trace(EC_INFO, `<--${this.modName}:FormatPIN ret=` + ret);
              resolve({ mod: this.modName, ret: "FatalError", param: [] });
            }
          }
        );
      }
    });
  }

  GetCpAlgorithms() {
    return new Promise<PinAlgorithms>((resolve, reject) => {
      this.mod.GetCpAlgorithms((ret: PinAlgorithms) => {
        resolve(ret);
      });
    });
  }

  setConfig(setting: Partial<PinSetting>) {
    merge(this.configs, setting);
  }

  getCpSM4() {
    return this.cpSM4;
  }
  getCpSM4MAC() {
    return this.cpSM4MAC;
  }

  async SMCapability() {
    let algArr = await this.GetCpAlgorithms();
    let useSM4 = this.configs.useSM4;
    if (useSM4) {
      for (let i = 0; i < algArr.length; i++) {
        if (algArr[i] == "SM4") {
          this.cpSM4 = true;
        } else if (algArr[i] == "SM4MAC") {
          this.cpSM4MAC = true;
        }
      }
    }
  }
}
